#include <render/vkrender.h>
//#include <vulkan/vulkan.h>
#include <cstring> //for strcmp()
#include <iostream>

#ifdef NDEBUG
const bool enableValidationLayers = false;
#else
const bool enableValidationLayers = true;
#endif

inline std::vector <const char*> validationLayers = { "VK_LAYER_KHRONOS_validation" };


inline bool checkValidationLayerSupport () {
    uint32_t layerCount;
    vkEnumerateInstanceLayerProperties( &layerCount, nullptr );

    std::vector <VkLayerProperties> availableLayers ( layerCount );
    vkEnumerateInstanceLayerProperties( &layerCount, availableLayers.data() );

    for (  const char* layerName : validationLayers ) {
        bool layerFound = false;
        for ( const auto& layerProperties : availableLayers ) {
            if ( strcmp ( layerName, layerProperties.layerName ) == 0 ) {
                layerFound = true;
                break;
            }
        }
        if ( !layerFound ) {
            return false;
        }
    }

    return true;
}

inline void vkrender::initWindow( Settings& settings ) {
    glfwInit();

    glfwWindowHint( GLFW_CLIENT_API, GLFW_NO_API );
    glfwWindowHint( GLFW_RESIZABLE, GLFW_FALSE );

    vkrender::window = glfwCreateWindow( settings.windowWidth, settings.windowHeight,
                                         settings.windowName.c_str(), nullptr, nullptr );
}

inline void vkrender::mainLoop() {
    while ( !glfwWindowShouldClose( vkrender::window ) ) {
        glfwPollEvents();
    }
}

inline void vkrender::destroyWindow() {
    vkDestroyInstance( vkrender::VKinstance, nullptr );
    glfwDestroyWindow( vkrender::window );
    glfwTerminate();
}

inline void vkrender::initilization( Application& app ) {
    if ( enableValidationLayers && !checkValidationLayerSupport() ) {
        LOG_INFO("validation layers requested, but not available!");
    }

    VkInstanceCreateInfo InstanceInfo = {};
    VkApplicationInfo applicationInfo = {};

    if ( enableValidationLayers ) {
        InstanceInfo.enabledLayerCount = static_cast<uint32_t>( validationLayers.size() );
        InstanceInfo.ppEnabledLayerNames = validationLayers.data();
    } else {
        InstanceInfo.enabledLayerCount = 0;
    }

    applicationInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
    applicationInfo.pApplicationName = app.appName.c_str();
    applicationInfo.applicationVersion = 1;
    applicationInfo.apiVersion = VK_MAKE_VERSION( 1, 0, 0 );

    InstanceInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
    InstanceInfo.pApplicationInfo = &applicationInfo;

    if ( vkCreateInstance( &InstanceInfo, nullptr, &this->VKinstance ) == VK_SUCCESS ) {
        LOG_DEBUG("Vulkan is ready!");
    } else {
        LOG_DEBUG("Error!");
    }

    uint32_t extensionCount = 0;
    vkEnumerateInstanceExtensionProperties( nullptr, &extensionCount, nullptr );
    vkrender::extensions.resize( extensionCount );
    vkEnumerateInstanceExtensionProperties( nullptr, &extensionCount, vkrender::extensions.data() );

    std::cout << "available extensions:" << std::endl;
    for ( const auto& extension : vkrender::extensions ) {
        std::cout << extension.extensionName << std::endl;
    }


    uint32_t deviceCount = 0;
    if ( vkEnumeratePhysicalDevices( this->VKinstance, &deviceCount, nullptr ) == VK_SUCCESS ) {
        std::cout << "Devices enumerated" << std::endl;
        vkrender::v_physicalDevices.resize( deviceCount );
        vkEnumeratePhysicalDevices( vkrender::VKinstance, &deviceCount, &v_physicalDevices[0] );
    }


    threadControl Threads;

    LOG_DEBUG("init glfw window");
    vkrender::initWindow( app.settings );
    
    LOG_DEBUG("Main loop");
    vkrender::mainLoop();

    vkrender::destroyWindow();
    LOG_DEBUG("Destroy window");
}
